﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Gibbed.IO;
using AmaroK86.MassEffect3.PCC;

namespace AmaroK86.MassEffect3.UnrealObjects
{
    // base type
    public abstract class UnrealType
    {
        protected PCCObject pccRef;
        public string Name     { get; internal set; }
        public string TypeName { get; internal set; }
        public ulong  Flags    { get; internal set; }
        public object Value    { get; set; }

        protected UnrealType(PCCObject pcc, string Name, ulong Flags, object Value)
        {
            pccRef = pcc;
            this.Name = Name;
            this.Flags = Flags;
            this.Value = Value;
        }

        public abstract byte[] ToArray();
    }

    public class IntType : UnrealType
    {
        public IntType(PCCObject pcc, string Name, ulong Flags, int val)
            : base(pcc, Name, Flags, val) { TypeName = "IntProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS32((int)Value);
            return buffer.ToArray();
        }
    }

    public class FloatType : UnrealType
    {
        public FloatType(PCCObject pcc, string Name, ulong Flags, float val)
            : base(pcc, Name, Flags, val) { TypeName = "FloatProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueF32((float)Value);
            return buffer.ToArray();
        }
    }

    public class StringRefType : UnrealType
    {
        public StringRefType(PCCObject pcc, string Name, ulong Flags, int val)
            : base(pcc, Name, Flags, val) { TypeName = "StringRefProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS32((int)Value);
            return buffer.ToArray();
        }
    }

    public class BoolType : UnrealType
    {
        public BoolType(PCCObject pcc, string Name, ulong Flags, bool val)
            : base(pcc, Name, Flags, val) { TypeName = "BoolProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueB8((bool)Value);
            return buffer.ToArray();
        }
    }

    public class NameType : UnrealType
    {
        public NameType(PCCObject pcc, string Name, ulong Flags, string val)
            : base(pcc, Name, Flags, val) { TypeName = "NameProperty"; }

        public override byte[] ToArray()
        {
            if (!pccRef.Names.Exists(name => name == (string)Value))
                pccRef.Names.Add((string)Value);

            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS64(pccRef.Names.IndexOf((string)Value));
            return buffer.ToArray();
        }
    }

    public class StrType : UnrealType
    {
        public StrType(PCCObject pcc, string Name, ulong Flags, string val)
            : base(pcc, Name, Flags, val) { TypeName = "StrProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            string str = (string)Value;
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueS32(((str.Length + 1) * 2) + 4);
            buffer.WriteValueS32(0);
            buffer.WriteValueS32(-(str.Length + 1));
            buffer.WriteString(str + "\0", (uint)(str.Length + 1) * 2, Encoding.Unicode);
            return buffer.ToArray();
        }
    }

    public class ByteType : UnrealType
    {
        public string ByteName;
        public int val2;

        public ByteType(PCCObject pcc, string Name, ulong Flags, string BName, string val, int val2)
            : base(pcc, Name, Flags, val)
        {
            TypeName = "ByteProperty";
            ByteName = BName;
            this.val2 = val2;
        }

        public override byte[] ToArray()
        {
            if (!pccRef.Names.Exists(name => name == (string)Value))
                pccRef.Names.Add((string)Value);

            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS64(pccRef.Names.IndexOf(ByteName));
            if (Flags != 1)
            {
                buffer.WriteValueS32(pccRef.Names.IndexOf((string)Value));
                buffer.WriteValueS32(val2);
            }
            else
                buffer.WriteValueS8((sbyte)val2);
            return buffer.ToArray();
        }
    }

    public class ObjectType : UnrealType
    {
        public ObjectType(PCCObject pcc, string Name, ulong Flags, int val)
            : base(pcc, Name, Flags, val) { TypeName = "ObjectProperty"; }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS32((int)Value);
            return buffer.ToArray();
        }
    }

    public class ArrayType : UnrealType
    {
        public ArrayType(PCCObject pcc, string Name, ulong Flags, int[] val)
            : base(pcc, Name, Flags, val) { TypeName = "ArrayProperty"; }

        public override byte[] ToArray()
        {
            throw new NotImplementedException();
        }
    }

    public class StructType : UnrealType
    {
        public string StructName;
        public StructType(PCCObject pcc, string Name, ulong Flags, string SName, byte[] val)
            : base(pcc, Name, Flags, val)
        {
            TypeName = "StructProperty";
            StructName = SName;
        }

        public override byte[] ToArray()
        {
            MemoryStream buffer = new MemoryStream();
            buffer.WriteValueS64(pccRef.Names.IndexOf(Name));
            buffer.WriteValueS64(pccRef.Names.IndexOf(TypeName));
            buffer.WriteValueU64(Flags);
            buffer.WriteValueS64(pccRef.Names.IndexOf(StructName));
            buffer.Write((byte[])Value, 0, ((byte[])Value).Length);
            return buffer.ToArray();
        }
    }
}
